/* 
 * File:   Solver.cpp
 * Author: Kenley
 * 
 * Created on October 12, 2012, 9:56 AM
 * 
 * See Solver.h for comments on each method.
 */

#include <iostream>
#include <fstream>
//#include <map>
#include "Solver.h"
#include "mathomatic.h"

using namespace std;

Solver::Solver() {
    if (!matho_init()) {
        cout << "Not enough memory." << endl;
        // TODO throw an exception
    } else {
//        cout << "Solver Constructed successfully." << endl;
    }
}

Solver::~Solver() {
    free_mem();
}

string Solver::solveFor(string equation, string variable) const {
    // TODO validate equation and variable.
    // TODO check return from matho_parse and matho_process.
    // and throw exceptions as needed.
    char *output;
    matho_parse((char*) equation.c_str(), NULL);
    matho_process((char*) string("solve " + variable).c_str(), &output);
    matho_clear();

    if (output) {
        return simplify(string(output));
    } else {
        return "[Empty string]";
        // or throw and exception
    }
    // Possible results
    // "No work done." if it is not an equation.
    // 
}

string Solver::replace(string equation, string variable, string expression) const {
    // TODO validate equation and variable and expression.
    // TODO check return from matho_parse and matho_process.
    // and throw exceptions as needed.
    char *output;
    matho_parse((char*) equation.c_str(), NULL);
    matho_process((char*) string("replace " + variable + " with " + expression).c_str(), &output);
    matho_clear();

    if (output) {
        return string(output);
    } else {
        return "";
        // or throw and exception
    }
}

string Solver::simplify(const string& equation) const {
    // TODO validate equation
    // TODO check return from matho_parse and matho_process.
    // and throw exceptions as needed.

    // sample out put for x=2*sign
    //Solution number 1 with sign0 = 1:
    //#2: x = 2
    //Solution number 2 with sign0 = -1:
    //#3: x = -2
    //2 unique solutions stored in equation spaces for this expression (#1).
    //x = 2*sign0

    // sample out put for x=2*sign0+.25*sign1
    //Solution number 1 with sign0 = 1, sign1 = 1:
    //#2: x = 9/4
    //Solution number 2 with sign0 = -1, sign1 = 1:
    //#3: x = -7/4
    //Solution number 3 with sign0 = 1, sign1 = -1:
    //#4: x = 7/4
    //Solution number 4 with sign0 = -1, sign1 = -1:
    //#5: x = -9/4
    //4 unique solutions stored in equation spaces for this expression (#1).
    //x = (2*sign0) + (sign1/4)

    // at this point I am not going to support sign since it dumps most of the 
    // interesting information to the terminal. It looks like I will have to find
    // a way to catch the messages sent to the terminal, or I will have to parse
    // out the sign s my self and pass them in one at a time.
    // I could cur_equation or result_en does not work since even if it uses
    // the next few spaces it will not change them. Perhaps I could parse a new
    // one after doing the simplify and find what space I am in then I would know 
    // that all between them were used for answers for the simplify.

    char *output;
    matho_parse((char*) equation.c_str(), &output); // cur_equation result_en
    //    cout << "&output 1 = " << output << endl;
    //    cout << "current equation space 1= " << cur_equation << endl;
    //    cout << "result in 1= " << result_en << endl;
    matho_process((char*) string("simplify sign").c_str(), &output);
    //    cout << "&output 2 = " << output << endl;
    //    cout << "current equation space 2= " << cur_equation << endl;
    //    cout << "result in 2= " << result_en << endl;
    matho_clear();
    //    cout << "current equation space 3= " << cur_equation << endl;
    //    cout << "result in 3= " << result_en << endl;

    if (output) {
        return string(output);
    } else {
        return "[empty string]";
        // or throw and exception
    }

}

string Solver::solveExact(string equation, map<string, string> variables) const {
    // To make up for the lack of the calculate command, the replace, approximate,
    // and "simplify sign" commands are provided, allowing very similar functionality.

    // the map is going to have first the variable and then the value
    // will need to call replace for every variables
    
    // on 5 feb 2013 I changed this so it would return a single solution rather 
    // then a set of solutions. SIGN or +/- is not fully supported, but when it 
    // is this function should just return an unsolved sign.


    // set no fractions_display
    char* output;
    matho_process((char*) string("set no fractions_display").c_str(), &output);
    matho_process((char*) string("set fractions=simple").c_str(), &output);
    //    cout << "output = " << output << endl;
    //    matho_clear(); set fractions=simple
    string stReturn = equation;
    map<string, string>::iterator it;
    for (it = variables.begin(); it != variables.end(); it++) {
        //        cout << (*it).first << " => " << (*it).second << endl;
        stReturn = replace(stReturn, (*it).first, (*it).second);
        //        cout << "the result = " << stReturn << endl;
    }
    // then will need to call simplify

    // multable solutions are not supported at this time if there is a "sign"
    // in the equation we will just return the unsolved equation
//    set<string> myset;
//    myset.insert(simplify(stReturn));
    return simplify(stReturn);
}

set<string> Solver::solveApproximate(string equation, map<string, string> variables) const {
    // Set up Mathomatic to return value as a decimal.
    matho_process((char*) string("set no fractions_display").c_str(), NULL);

    string stReturn = equation;
    map<string, string>::iterator it;
    for (it = variables.begin(); it != variables.end(); it++) {
        //        cout << (*it).first << " => " << (*it).second << endl;
        stReturn = replace(stReturn, (*it).first, (*it).second);
        //        cout << "the result = " << stReturn << endl;
    }

    // multable solutions are not supported at this time if there is a "sign"
    // in the equation we will just return the unsolved equation
    set<string> myset;
    myset.insert(simplify(stReturn));

    // Reset Mathomatic to return values as a fraction when possible.
    matho_process((char*) string("set fractions=simple").c_str(), NULL);
    return myset;
}

set<string> Solver::getVariables(string equation) const {
    char *output;
    matho_parse((char*) equation.c_str(), &output);
    // get variables on the last equation entered and send them to variables.txt file
    matho_process((char*) string("variables > variables.txt").c_str(), &output);
    matho_clear();

    // get the data from the file and put it into vars set.
    set<string> vars;
    string oneVar;
    ifstream myfile;
    myfile.open("variables.txt");
    while (!myfile.eof()) // To get you all the lines.
    {
        getline(myfile, oneVar); // Saves the line in oneVar
        if (oneVar.length() > 0) { // make sure it is not an empty line.
            vars.insert(oneVar); // add it to set of variables
        }
    }
    myfile.close();

    return vars;
}
